package com.ruyiadmin.springboot.service.impls.business.module;

import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.URLUtil;
import cn.hutool.extra.servlet.ServletUtil;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruyiadmin.springboot.common.awares.core.RuYiAdminContextAware;
import com.ruyiadmin.springboot.common.beans.system.SystemConfig;
import com.ruyiadmin.springboot.common.beans.system.SystemRedisConfig;
import com.ruyiadmin.springboot.common.components.core.RuYiRedisComponent;
import com.ruyiadmin.springboot.common.core.business.enums.DeletionType;
import com.ruyiadmin.springboot.common.core.business.enums.OperationType;
import com.ruyiadmin.springboot.common.core.business.enums.UserStatus;
import com.ruyiadmin.springboot.common.core.system.entities.ActionResult;
import com.ruyiadmin.springboot.common.core.system.entities.QueryCondition;
import com.ruyiadmin.springboot.common.core.system.entities.QueryResult;
import com.ruyiadmin.springboot.common.events.system.SysLogEvent;
import com.ruyiadmin.springboot.common.exceptions.RuYiAdminCustomException;
import com.ruyiadmin.springboot.common.utils.core.RuYiAesUtil;
import com.ruyiadmin.springboot.common.utils.core.RuYiHashUtil;
import com.ruyiadmin.springboot.common.utils.core.RuYiRsaUtil;
import com.ruyiadmin.springboot.domain.dto.business.module.BizAccountDTO;
import com.ruyiadmin.springboot.domain.dto.business.module.BizModuleDTO;
import com.ruyiadmin.springboot.domain.dto.business.module.BizUserModuleDTO;
import com.ruyiadmin.springboot.domain.dto.system.LoginDTO;
import com.ruyiadmin.springboot.domain.entity.business.module.BizAccount;
import com.ruyiadmin.springboot.domain.entity.business.module.BizModule;
import com.ruyiadmin.springboot.domain.entity.business.module.BizUser;
import com.ruyiadmin.springboot.domain.entity.business.module.BizUserModule;
import com.ruyiadmin.springboot.domain.entity.system.SysLog;
import com.ruyiadmin.springboot.repository.business.module.IBizAccountRepository;
import com.ruyiadmin.springboot.repository.business.module.IBizModuleRepository;
import com.ruyiadmin.springboot.repository.business.module.IBizUserModuleRepository;
import com.ruyiadmin.springboot.repository.business.module.IBizUserRepository;
import com.ruyiadmin.springboot.service.iservices.business.module.IBizUserModuleService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import eu.bitwalker.useragentutils.UserAgent;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.modelmapper.ModelMapper;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 模块与用户关系表 服务实现类
 * </p>
 *
 * @author RuYiAdmin
 * @since 2022-07-12
 */
@Service
@RequiredArgsConstructor
public class BizUserModuleServiceImpl extends ServiceImpl<IBizUserModuleRepository, BizUserModule>
        implements IBizUserModuleService {

    //region 服务实现类私有属性

    private final SystemConfig systemConfig;
    private final RuYiRedisComponent redisUtils;
    private final SystemRedisConfig systemRedisConfig;
    private final ModelMapper modelMapper;
    private final IBizUserModuleRepository bizUserModuleRepository;
    private final IBizUserRepository bizUserRepository;
    private final IBizModuleRepository bizModuleRepository;
    private final IBizAccountRepository bizAccountRepository;

    //endregion

    //region 查询用户与模块信息

    /**
     * <p>
     * 查询用户与模块信息
     * </p>
     *
     * @param queryCondition 查询条件
     * @return 查询结果
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public QueryResult<BizUserModuleDTO> queryUserModule(QueryCondition queryCondition) {
        QueryWrapper<BizUserModuleDTO> wrapper = new QueryWrapper<>();
        queryCondition.getQueryWrapper(wrapper);

        Page<BizUserModuleDTO> page = new Page<>(queryCondition.getPageIndex(), queryCondition.getPageSize());
        IPage<BizUserModuleDTO> records = this.bizUserModuleRepository.queryUserModule(page, wrapper);

        return QueryResult.success(records.getTotal(), records.getRecords());
    }

    //endregion

    //region 统一认证用户登录

    /**
     * 统一认证用户登录
     *
     * @param login 登录信息
     * @return 执行结果
     */
    @Override
    public ActionResult logon(LoginDTO login) throws Exception {
        Map<String, Object> obj = new HashMap<>();
        String privateKey = this.systemConfig.getRsaPrivateKey();

        //region 用户信息解密

        login.setUserName(RuYiRsaUtil.decrypt(login.getUserName(), privateKey));
        login.setPassword(RuYiRsaUtil.decrypt(login.getPassword(), privateKey));
        login.setCaptchaId(RuYiRsaUtil.decrypt(login.getCaptchaId(), privateKey));
        login.setCaptcha(RuYiRsaUtil.decrypt(login.getCaptcha(), privateKey));

        //endregion

        Object resultNum = this.redisUtils.get(login.getCaptchaId());
        if (resultNum != null && resultNum.toString().equals(login.getCaptcha())) {
            //删除验证码
            this.redisUtils.del(login.getCaptchaId());

            //region 获取用户信息

            List<BizUser> users = this.bizUserRepository.selectList(new QueryWrapper<>());

            //用户名去盐
            login.setUserName(login.getUserName().replace("_" + login.getCaptchaId(), ""));
            //密码去盐
            login.setPassword(login.getPassword().replace("_" + login.getCaptchaId(), ""));

            List<BizUser> tempUsers = users.stream()
                    .filter(t -> t.getIsdel() == 0)
                    .filter(t -> t.getUserLogonName().equals(login.getUserName()))
                    .collect(Collectors.toList());
            BizUser tempUser = tempUsers.size() > 0 ? tempUsers.get(0) : null;
            if (tempUser == null) {
                throw new RuYiAdminCustomException("user is invalid");
            }

            //AesKey
            String aesKey = systemConfig.getAesKey();
            //AES加密
            login.setPassword(RuYiAesUtil.encrypt(login.getPassword() + tempUser.getSalt(), aesKey));

            List<BizUser> list = users.stream().
                    filter(t -> t.getIsdel() == 0).
                    filter(t -> t.getUserLogonName().equals(login.getUserName())).
                    filter(t -> t.getUserPassword().equals(login.getPassword())).
                    filter(t -> t.getIsEnabled() == UserStatus.Enabled.ordinal())
                    .collect(Collectors.toList());

            //endregion

            //用户合法
            if (list.size() == 1) {
                BizUser user = list.get(0);
                UUID token = UUID.randomUUID();
                int tokenExpiration = this.systemConfig.getUserTokenExpiration();

                //region 获取授权信息

                List<BizModule> modules = this.bizModuleRepository.selectList(new QueryWrapper<>());
                List<BizUserModule> userModules = this.bizUserModuleRepository.selectList(new QueryWrapper<>());
                userModules = userModules.stream()
                        .filter(t -> t.getUserId().equals(user.getId()))
                        .collect(Collectors.toList());

                List<BizModuleDTO> permissions = new ArrayList<>();
                for (BizModule module : modules) {
                    BizModuleDTO moduleDTO = new BizModuleDTO();
                    moduleDTO.setId(module.getId());
                    moduleDTO.setModuleName(module.getModuleName());
                    moduleDTO.setModuleShortName(module.getModuleShortName());
                    moduleDTO.setModuleShortNameEn(module.getModuleShortNameEn());
                    moduleDTO.setModuleProtocol(module.getModuleProtocol());
                    moduleDTO.setModuleAddress(module.getModuleAddress());
                    moduleDTO.setModulePort(module.getModulePort());
                    moduleDTO.setModuleLogoAddress(module.getModuleLogoAddress());
                    moduleDTO.setModuleSsoAddress(module.getModuleSsoAddress());
                    moduleDTO.setModuleTodoAddress(module.getModuleTodoAddress());
                    moduleDTO.setSerialNumber(module.getSerialNumber());

                    List<BizUserModule> userModuleList = userModules.stream()
                            .filter(t -> t.getModuleId().equals(module.getId()))
                            .collect(Collectors.toList());
                    BizUserModule auth = userModuleList.size() > 0 ? userModuleList.get(0) : null;
                    if (auth != null) {
                        moduleDTO.setUserModuleLogonName(auth.getUserModuleLogonName());
                        moduleDTO.setUserModulePassword(RuYiHashUtil.SHA512(auth.getUserModulePassword() + token));
                    }

                    permissions.add(moduleDTO);
                }

                //endregion

                //region 写入缓存信息

                String key = this.systemRedisConfig.getUnifiedAuthenticationPattern() + user.getId() + "_";
                String pattern = String.format("%s*", key);
                List<String> keys = this.redisUtils.scan(pattern);
                if (keys.size() > 0) {
                    for (String item : keys) {
                        //删除用户token
                        this.redisUtils.del(item);
                    }
                }

                int expiration = tokenExpiration * 60;
                this.redisUtils.set(key + token, JSON.toJSONString(user), expiration);

                //endregion

                //region 记录登录日志

                //获取请求url,ip,httpMethod
                HttpServletRequest request = ((ServletRequestAttributes) Objects
                        .requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();

                UserAgent userAgent = UserAgent.parseUserAgentString(request.getHeader("user-agent"));
                String clientType = userAgent.getOperatingSystem().getDeviceType().toString();//客户端类型  手机、电脑、平板
                String os = userAgent.getOperatingSystem().getName();//操作系统类型
                String browser = userAgent.getBrowser().toString();//浏览器类型

                SysLog sysLog = new SysLog();

                sysLog.setId(UUID.randomUUID().toString());
                sysLog.setUserId(user.getId());
                sysLog.setUserName(user.getUserDisplayName() + "/" + user.getUserLogonName());
                sysLog.setOrgId(StringUtils.EMPTY);
                sysLog.setOrgName(StringUtils.EMPTY);
                sysLog.setSystem(clientType + "，" + os);
                sysLog.setBrowser(browser);
                sysLog.setIp(ServletUtil.getClientIP(request));
                sysLog.setRequestMethod(request.getMethod());
                sysLog.setRequestUrl(URLUtil.getPath(request.getRequestURI()));
                sysLog.setParams(HttpUtil.toParams(request.getParameterMap()));
                sysLog.setResult(StringUtils.EMPTY);
                sysLog.setOldValue(StringUtils.EMPTY);
                sysLog.setNewValue(StringUtils.EMPTY);
                sysLog.setRemark(sysLog.getUserName() + "于" + LocalDateTimeUtil.now()
                        + "访问了" + sysLog.getRequestUrl() + "接口");
                sysLog.setIsdel(DeletionType.Undeleted.ordinal());
                sysLog.setCreator(user.getId());
                sysLog.setCreateTime(LocalDateTimeUtil.now());
                sysLog.setModifier(user.getId());
                sysLog.setModifyTime(LocalDateTimeUtil.now());
                sysLog.setVersionId(UUID.randomUUID().toString());
                sysLog.setOperationType(OperationType.UnifiedAuthentication.ordinal());

                // 发送异步日志事件
                RuYiAdminContextAware.publishEvent(new SysLogEvent(sysLog));

                //endregion

                user.setUserPassword(null);//擦除敏感信息

                obj.put("user", user);
                obj.put("permissions", permissions);
                obj.put("token", token);

            } else {
                throw new RuYiAdminCustomException("user is invalid");
            }
        } else {
            throw new RuYiAdminCustomException("captcha is invalid");
        }

        return ActionResult.success(JSON.toJSON(obj));
    }

    //endregion

    //region 匿名获取同步口令

    /**
     * 匿名获取同步口令
     *
     * @param login 登录信息
     * @return ActionResult
     */
    @Override
    public ActionResult getToken(LoginDTO login) throws Exception {
        ActionResult actionResult = new ActionResult();
        String privateKey = this.systemConfig.getRsaPrivateKey();

        //region 用户信息解密

        login.setUserName(RuYiRsaUtil.decrypt(login.getUserName(), privateKey));
        login.setPassword(RuYiRsaUtil.decrypt(login.getPassword(), privateKey));
        login.setCaptchaId(RuYiRsaUtil.decrypt(login.getCaptchaId(), privateKey));

        //endregion

        Object resultNum = this.redisUtils.get(login.getCaptchaId());
        if (resultNum != null && resultNum.toString().equals(login.getCaptcha())) {
            //删除验证码
            this.redisUtils.del(login.getCaptchaId());

            //region 获取用户信息

            //用户名去盐
            login.setUserName(login.getUserName().replace("_" + login.getCaptchaId(), ""));
            //密码去盐
            login.setPassword(login.getPassword().replace("_" + login.getCaptchaId(), ""));

            //AesKey
            String aesKey = systemConfig.getAesKey();
            //AES加密
            login.setPassword(RuYiAesUtil.encrypt(login.getPassword(), aesKey));

            //获取用户信息
            List<BizAccount> accounts = this.bizAccountRepository.selectList(new QueryWrapper<>());
            List<BizAccount> list = accounts.stream().
                    filter(t -> t.getIsdel() == 0).
                    filter(t -> t.getUserLogonName().equals(login.getUserName())).
                    filter(t -> t.getUserPassword().equals(login.getPassword())).
                    filter(t -> t.getIsEnabled() == UserStatus.Enabled.ordinal())
                    .collect(Collectors.toList());

            //endregion

            //用户合法
            if (list.size() == 1) {
                BizAccount account = list.get(0);
                BizModule module = this.bizModuleRepository.selectById(account.getModuleId());

                String token = UUID.randomUUID().toString();
                int tokenExpiration = this.systemConfig.getUserTokenExpiration();
                int expiration = tokenExpiration * 60;

                BizAccountDTO accountDTO = this.modelMapper.map(account, BizAccountDTO.class);
                accountDTO.setToken(token);

                this.redisUtils.set(this.systemRedisConfig.getSynchronizationPattern() + token,
                        JSON.toJSONString(accountDTO), expiration);

                actionResult.setHttpStatusCode(HttpStatus.OK.value());
                actionResult.setObject(token);
                actionResult.setMessage("OK");

                //region 记录登录日志

                //获取请求url,ip,httpMethod
                HttpServletRequest request = ((ServletRequestAttributes) Objects
                        .requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();

                UserAgent userAgent = UserAgent.parseUserAgentString(request.getHeader("user-agent"));
                String clientType = userAgent.getOperatingSystem().getDeviceType().toString();//客户端类型  手机、电脑、平板
                String os = userAgent.getOperatingSystem().getName();//操作系统类型
                String browser = userAgent.getBrowser().toString();//浏览器类型

                SysLog sysLog = new SysLog();

                sysLog.setId(UUID.randomUUID().toString());
                sysLog.setUserId(account.getId());
                sysLog.setUserName(account.getUserDisplayName() + "/" + account.getUserLogonName());
                sysLog.setOrgId(module.getId());
                sysLog.setOrgName(module.getModuleShortName());
                sysLog.setSystem(clientType + "，" + os);
                sysLog.setBrowser(browser);
                sysLog.setIp(ServletUtil.getClientIP(request));
                sysLog.setRequestMethod(request.getMethod());
                sysLog.setRequestUrl(URLUtil.getPath(request.getRequestURI()));
                sysLog.setParams(HttpUtil.toParams(request.getParameterMap()));
                sysLog.setResult(StringUtils.EMPTY);
                sysLog.setOldValue(StringUtils.EMPTY);
                sysLog.setNewValue(StringUtils.EMPTY);
                sysLog.setRemark(sysLog.getUserName() + "于" + LocalDateTimeUtil.now()
                        + "访问了" + sysLog.getRequestUrl() + "接口");
                sysLog.setIsdel(DeletionType.Undeleted.ordinal());
                sysLog.setCreator(account.getId());
                sysLog.setCreateTime(LocalDateTimeUtil.now());
                sysLog.setModifier(account.getId());
                sysLog.setModifyTime(LocalDateTimeUtil.now());
                sysLog.setVersionId(UUID.randomUUID().toString());
                sysLog.setOperationType(OperationType.GetSyncToken.ordinal());

                // 发送异步日志事件
                RuYiAdminContextAware.publishEvent(new SysLogEvent(sysLog));

                //endregion

            } else {
                throw new RuYiAdminCustomException("account is invalid");
            }
        } else {
            throw new RuYiAdminCustomException("salt is invalid");
        }

        return actionResult;
    }

    //endregion

}
